﻿using System;
using biz.ritter.javapi.util;
using biz.ritter.javapi.lang;
using Nofs.Net.nofs.metadata.interfaces;
using biz.ritter.javapi.net;
using biz.ritter.javapi.io;
using Nofs.Net.AnnotationDriver;

namespace Nofs.Net.nofs.metadata.XMLDriver
{

    public sealed class XMLClassLoader : INoFSClassLoader
    {
        private ClassLoader _loader;
        private Map<String, Class> _classCache;
        private XMLFileSystem _fs;

        private XMLClassLoader()
        {
            _classCache = new HashMap<String, Class>();
        }

        public XMLClassLoader(XMLFileSystem xmlFileSystem, ClassLoader loader) //throws ClassNotFoundException 
            : this()
        {
            _fs = xmlFileSystem;
            _loader = loader;
            LoadAllClasses();
        }

        public XMLClassLoader(XMLFileSystem xmlFileSystem, String fileName) //throws MalformedURLException, ClassNotFoundException 
            : this()
        {
            _fs = xmlFileSystem;
            URL[] url = new URL[] { new URL(fileName) };
            _loader = new URLClassLoader(url, ClassLoader.getSystemClassLoader());
            LoadAllClasses();
        }

        public Object Create(String className) //throws InstantiationException, IllegalAccessException, ClassNotFoundException 
        {
            return LoadClass(className).newInstance();
        }

        //@SuppressWarnings("unchecked")
        private void LoadAllClasses() //throws ClassNotFoundException 
        {
            Map<String, Set<Class>> classes = ClassList.findClasses(_loader);
            String[] names = new String[classes.keySet().size()];
            classes.keySet().toArray<String>(names);
            
            foreach (String interfaceName in names)
            {
                LinkedList<Class> list = new LinkedList<Class>();
                list.addAll(classes.get(interfaceName));
                foreach (Class cls in list)
                {
                    if (!_classCache.containsKey(cls.getName()))
                    {
                        _classCache.put(cls.getName(), cls);
                    }
                }
            }
        }

        public Class LoadClass(String className) //throws ClassNotFoundException
        {
            Class cls;
            try
            {
                if (_classCache.containsKey(className))
                {
                    cls = _classCache.get(className);
                }
                else
                {
                    cls = _loader.loadClass(className);
                    _classCache.put(className, cls);
                }
            }
            catch (ClassNotFoundException cnfe)
            {
                String[] names = new String[_classCache.keySet().size()];
                _classCache.keySet().toArray<String>(names);

                foreach (String clsName in names)
                {
                    Console.WriteLine("known class: " + clsName);
                }
                throw cnfe;
            }
            return cls;
        }

        public LinkedList<Class> LoadClassesWithAnnotation(MarkerTypes markerType) //throws ClassNotFoundException, Exception 
        {
            LinkedList<Class> classes = new LinkedList<Class>();
            if (MarkerTypes.RootFolderObject == markerType)
            {
                classes.add(LoadClass(_fs.RootFolderClassName()));
            }
            else if (MarkerTypes.DomainObject == markerType)
            {
                foreach (XMLDomainObject domainObj in _fs.DomainObjects())
                {
                    classes.add(LoadClass(domainObj.ClassName()));
                }
            }
            else if (MarkerTypes.FolderObject == markerType)
            {
                foreach (XMLDomainObject domainObj in _fs.DomainObjects())
                {
                    if (domainObj.FolderDescription() != null)
                    {
                        classes.add(LoadClass(domainObj.ClassName()));
                    }
                }
            }
            else
            {
                throw new System.Exception("unhandled marker type");
            }
            return classes;
        }

    }
}
